#pragma once

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <algorithm>
#include <mutex>
#include <cassert>
#include <jsoncpp/json/json.h>

#include "../comm/util.hpp"
#include "../comm/log.hpp"
#include "../comm/httplib.h"
#include "oj_model2.hpp"
#include "oj_view.hpp"

namespace ns_control
{
    using namespace std;
    using namespace ns_log;
    using namespace ns_util;
    using namespace ns_model;
    using namespace httplib;
    using namespace ns_view;
    //提供服务的主机
    class Machine
    {
    public:
        std::string  _ip;
        int _port;
        uint64_t load;//服务器负载
        std::mutex *mtx;//禁止拷贝
    public:
        Machine()
        :_ip("0")
        ,_port(0)
        ,load(0)
        ,mtx(nullptr)
        {};
        void Incload()
        {
            if (mtx)
            {
                mtx->lock();
            }
            load++;
            if (mtx)
            {
                mtx->unlock();
            }
        }
        void Decload()
        {
            if (mtx)
            {
                mtx->lock();
            }
            load--;
            if (mtx)
            {
                mtx->unlock();
            }
        }
        void Resetload()
        {
            if (mtx)
            {
                mtx->lock();
            }
            load=0;
            if (mtx)
            {
                mtx->unlock();
            }
        }
        ~Machine(){};
        uint64_t Load()
        {
            uint64_t _load=0;
            if (mtx)
            {
                mtx->lock();
            }
            _load=load;
            if (mtx)
            {
                mtx->unlock();
            }
            return _load;
        }
    };
    const std::string service_machine = "./conf/service_machine.conf";
    class LoadBalance
    {
    public:
        LoadBalance()
        {
            assert(LoadConf(service_machine));
            LOG(INFO) << "加载 " << service_machine << " 成功"<< "\n";
        }
        ~LoadBalance()
        {}
        bool LoadConf(string machine_conf)
        {
            ifstream in(machine_conf);
            if (!in.is_open())
            {
                LOG(ERROR) << "没有找到主机列表文件" << "\n";
                return false;
            }
            string line;
            while (getline(in, line))
            {
                vector<string> tokens;
                StringUtil::Split(line, &tokens, ":");
                if (tokens.size() != 2)
                {
                    LOG(WARNING) << " 切分 " << line << " 失败"<< "\n";
                    continue;
                }
                Machine machine;
                machine._ip = tokens[0];
                machine._port = atoi(tokens[1].c_str());
                machine.load=0;
                machine.mtx=new mutex();
                online.push_back(_machines.size());
                _machines.push_back(machine);
            }
            in.close();
            return true;
        }
        bool SmartChoice(int* id,Machine** m)
        {
            if (online.size() == 0)
            {
                LOG(ERROR) << "没有在线主机" << "\n";
                return false;
            }
            int min_load = INT_MAX;
            for (auto i : online)
            {
                if (_machines[i].Load() < min_load)
                {
                    min_load = _machines[i].Load();
                    *id = i;
                    *m = &_machines[i];
                }
            }
            return true;
        }
        void OfflineMachine(int which)
        {
            mtx.lock();
            for(auto item=online.begin();item!=online.end();item++)
            {
                if(*item==which)
                {
                    _machines[which].Resetload();
                    offline.push_back(which);
                    online.erase(item);
                    break;
                }
            }
            mtx.unlock();
        }
        void OnlineMachine()
        {
            mtx.lock();
            online.insert(online.end(),offline.begin(),offline.end());
            offline.erase(offline.begin(),offline.end());
            mtx.unlock();
            LOG(INFO) << "所有的主机有上线啦!" << "\n";
        }
        void ShowMachines()
        {
            mtx.lock();
            std::cout << "当前在线主机列表: ";
            for(auto &id : online)
            {
                std::cout << id << " ";
            }
            std::cout << std::endl;
            std::cout << "当前离线主机列表: ";
            for(auto &id : offline)
            {
                std::cout << id << " ";
            }
            std::cout << std::endl;
            mtx.unlock();
        }
    private:
        vector<Machine> _machines;
        vector<int> online;//在线主机id
        vector<int> offline;//离线主机id
        std::mutex mtx;

    };

    class Control
    {
    public:
        Control(){};
        ~Control(){};
        void RecoverMachine()
        {
            _loadbalance.OnlineMachine();
        }
        bool AllQuestions(string* html)
        {
            bool ret;
            vector<Question> questions;
            if (model.GetAllQuestions(&questions))
            {
                view.AllExpandHtml(questions, html);
                return true;
            }
            {
                return false;
            }
        }
        bool GetQuestion(const std::string &question_id,string*html)
        {
            struct Question question;
            if (model.GetOneQuestion(question_id, &question))
            {
                view.OneExpandHtml(question, html);
                return true;
            }
            else
            {
                return false;
            }
        }
        void Judge(const std::string &number, const std::string in_json, std::string *out_json)
        {
            struct  Question q;
            model.GetOneQuestion(number,&q);
            Json::Value invalue;
            Json::Reader reader;
            reader.parse(in_json, invalue);
            string code=invalue["code"].asString();
            Json::Value compile_value;
            compile_value["inpute"]=invalue["input"].asString();
            compile_value["code"]=code+"\n"+q.tail;
            compile_value["cpu_limit"]=q.cpu_limit;
            compile_value["mem_limit"]=q.mem_limit;
            Json::FastWriter writer;
            string compile_json=writer.write(compile_value);
            while(true)
            {
                int id=0;
                Machine* m=nullptr;
                if(!_loadbalance.SmartChoice(&id,&m))
                {
                    break;
                }
                m->Incload();
                Client client(m->_ip,m->_port);
                LOG(INFO) << " 选择主机成功, 主机id: " << id << " 详情: " << m->_ip << ":" << m->_port << " 当前主机的负载是: " << m->Load() << "\n";
                if (auto res= client.Post("/compile_and_run", compile_json, "application/json; charset=utf-8"))
                {
                    if (res->status == 200)
                    {
                        *out_json = res->body;
                        m->Decload();
                        LOG(INFO)<<"请求编译运行成功"<<"\n";
                        break;
                    }
                    m->Decload();
                }
                else
                {
                    LOG(ERROR) << "主机id: " << id << " 详情: " << m->_ip << ":" << m->_port << " 可能已离线 "<< "\n";
                    _loadbalance.OfflineMachine(id);
                    _loadbalance.ShowMachines();
                }
            }

        }
    private:
        Model model;
        View view;
        LoadBalance _loadbalance;
    };
}
